/**
 * File: ReadInfo.java
 * Created by: mhaimel
 * Created on: Mar 5, 2010
 * CVS:  $Id: ReadInfo.java 1.0 Mar 5, 2010 2:38:57 PM mhaimel Exp $
 */
package uk.ac.ebi.curtain.model.graph.curtain;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.lang.StringUtils;

/**
 * @author mhaimel
 *
 */
public class CategoryReadInfo {
	
	private final Integer category;
	private final AtomicReference<Double> weightSum;
	private final AtomicInteger multiplicity;
	private final AtomicInteger repeatMarkers;
	private final LinkedBlockingQueue<Integer> gapList; // TODO switch on again
	
	public CategoryReadInfo(Integer category) {
		this(category,0d,new Integer[0], 0,0);
	}
	
	public CategoryReadInfo(Integer category, double weightSum,Integer[] gaps, Integer multiplicity, Integer repeats) {
		this.category = category;
		this.weightSum = new AtomicReference<Double>(weightSum);
		this.multiplicity = new AtomicInteger(multiplicity);
		this.repeatMarkers = new AtomicInteger(repeats);
		this.gapList = new LinkedBlockingQueue<Integer>(Arrays.asList(gaps));
	}
	
	@Override
	public String toString() {
		return StringUtils.join(new Object[]{
				"multiplicity="+getMultiplicity(),
				"repeatMarkers="+getRepeatMarkers(),
				"weightSum="+weightSum.get(),
				"gapList="+gapList, 
		},';');
	}
	
	public Integer getCategory() {
		return category;
	}
	
	public void addRead(Double weight){
		incrementMultiplicity();
		incrementWeight(weight);
	}

	public void addRead(Integer gap, Double weight){
		addRead(weight);
		addGap(gap);
	}
	
	private void addGap(Integer gap){
		gapList.add(gap);
	}
	
	public void incrementMultiplicity(){
		this.multiplicity.incrementAndGet();
	}
	
	public Integer getMultiplicity(){
		return this.multiplicity.get();
	}
	
	public Double getWeightSum(){
		return weightSum.get();
	}
	
	public List<Integer> getGaps(){
		return new ArrayList<Integer>(gapList);
	}

	private Double incrementWeight(Double delta){
		while(true){
			Double val = weightSum.get();
			Double nVal = val+ delta;
			if(weightSum.compareAndSet(val, nVal)){
				return nVal;
			}
		}
	}

	public Integer[] arrayOfGaps(){
		return gapList.toArray(new Integer[0]);
	}
	
	public int getRepeatMarkers() {
		return repeatMarkers.get();
	}
	
	public void setRepeatMarkers(int repeatMarkers) {
		this.repeatMarkers.set(repeatMarkers);
	}
	
	public int incrementRepeatMarker(){
		return this.repeatMarkers.incrementAndGet();
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + category.hashCode();
		result = prime * result + Double.valueOf(weightSum.get()).hashCode();
		result = prime * result + Arrays.hashCode(arrayOfGaps());
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		CategoryReadInfo other = (CategoryReadInfo) obj;
		if(!this.category.equals(other.category))
			return false;
		if(!this.weightSum.get().equals(other.weightSum.get()))
			return false;
		if(!Arrays.equals(this.arrayOfGaps(), other.arrayOfGaps()))
			return false;
		return true;
	}	
}
